package plus.lgx.textsearcher.searcher;

import lombok.SneakyThrows;
import plus.lgx.textsearcher.config.SearcherConfig;
import plus.lgx.textsearcher.entity.FileWrapper;
import plus.lgx.textsearcher.searcher.handler.FileDispatcher;
import plus.lgx.textsearcher.util.FileUtil;

import java.io.File;
import java.util.Scanner;

import static plus.lgx.textsearcher.constant.Const.CURRENT_PATH;
import static plus.lgx.textsearcher.constant.Const.LS;
import static plus.lgx.textsearcher.global.Global.buffer;

// DONE: set buffer size, specified file suffix, show skipped
// DONE: output template
// DONE: generate html output file

// TODO: enhanced output template
// TODO: internationalization
// TODO: start up server, like jupyter notebook
// TODO: parse multiple types of files
// TODO: log level
// TODO: create text index

/**
 * Class name: TextSearcher
 *
 * Create time: 12/14/22 5:23 PM
 *
 * @author lgx
 * @version 1.0
 */
public class TextSearcher {

    /**
     * 包含有匹配的字符串的文件数量
     */
    private int fileNumber = 0;

    /**
     * 总共匹配的数量
     */
    private int matchedNumber = 0;

    /**
     * 跳过的文件数量
     */
    private int fileSkippedCount = 0;

    private final SearcherConfig config;

    public TextSearcher(SearcherConfig config) {
        this.config = config;
    }

    @SneakyThrows
    public void search() {

        FileDispatcher fd = new FileDispatcher(config);
        File[] files = fd.getInputFiles();

        System.out.format("Searching text \"%s\" from %d file%s.%n", config.searchContent,
                files.length, files.length > 1 ? "s" : "");

        buffer = new char[config.bufferSize];

        fd.printHead();
        search0(fd, files);
        fd.printTail();
        printDone();

    }

    private void search0(FileDispatcher fd, File[] files) {
        final boolean console = !config.noPause && config.console;
        Scanner scanner = new Scanner(System.in);
        for (int i = 0, filesLength = files.length; i < filesLength; i++) {
            File f = files[i];
            if (check(f)) {
                FileWrapper wrapper = new FileWrapper(f, fileNumber + 1, i + 1);
                fd.dispatch(wrapper);
                if (wrapper.hasMatched()) {
                    matchedNumber += wrapper.getMatchedNumber();

                    if (fileNumber++ == config.maxFileCount - 1) {
                        System.out.format("Exceed max file count %d.%n", config.maxFileCount);
                        return;
                    }

                    if (console && fileNumber % 5 == 0) {
                        System.out.println("press ENTER to continue, or use 'Ctrl + C' to interrupt... ");
                        scanner.nextLine();
                    }
                }
            }
        }
    }

    /**
     * 检查文件是否为空或大小是否超过缓冲区大小
     *
     * @param f 待检查的文件
     * @return true - 文件非空且大小未超过缓冲区大小<br>
     *         false - 其他情况
     */
    private boolean check(File f) {
        long len = f.length();
        if (len == 0) {
            printSkipped("Found an empty file \"%s\", skipped. " + LS, f);
            return false;
        }
        if (len > config.bufferSize) {
            printSkipped("Skipped a large file \"%s\", size: %s. " + LS, f, FileUtil.toFileLength(len));
            return false;
        }

        return true;
    }

    private void printSkipped(String fmt, Object... o) {
        if (fileSkippedCount < config.showSkipped) {
            o[0] = ((File) o[0]).getAbsolutePath().startsWith(CURRENT_PATH)
                    ? ((File) o[0]).getAbsolutePath().substring(CURRENT_PATH.length() + 1)
                    : ((File) o[0]).getAbsolutePath();
            System.err.format(fmt, o);
        } else if (fileSkippedCount == config.showSkipped) {
            System.out.format("Skipped %d files. ", fileSkippedCount);
        } else {
            System.out.format("%s%d files. ",
                    back(9 + (int) Math.log10(Math.max(1, fileSkippedCount - 1))),
                    fileSkippedCount);
        }
        fileSkippedCount++;
    }

    private static String back(int count) {
        return "\b".repeat(Math.max(0, count));
    }

    private void printDone() {
        System.out.format((fileSkippedCount > config.showSkipped ? "%n" : "")
                        + "Done. Found %d matched text%s in %d file%s.%n",
                matchedNumber, matchedNumber > 1 ? "s" : "", fileNumber, fileNumber > 1 ? "s" : "");
    }

}
